public class Score {

	public int octave = 4;
	public int chordPointer = 3;
	public int barPointer = 3;
	public int meter = 4;
	public boolean head = false;
	public boolean trans = false;

	// harmonic score
	public int key = 0; // based on pitch class of root ... C = 0
	public boolean isMinor = false; // is the key major?
	public int[][] changes = new int[3][1];

	// score data
	public int[][] instructions = null;
	public String[][] load = new String[2][0];

	public int length = 0;
	public int[][] pitch = new int[1][1];
	public int[][] onset = new int[1][1];
	public int repeat = 0;

	// threads
	public Thread threadWB = null;
	public WalkingBass WB = null;
	public Improvise improv = null;
	public Thread threadIM = null;
	public Chords chords = null;
	public Thread thread = null;
	
	// sync
	public boolean chordsDone = true;
	public boolean improvDone = true;
	public boolean WBDone = true;
	
	public boolean learning = false;
	public int waitBarScore = 0;

	public int run() {

		Main.noteOut = new int[3][]; // notes 'to be played' each bar
		Main.score.key = 0;
		Main.score.chordPointer++;
		Main.score.barPointer++;
		repeat = (int) Math.floor(Main.score.barPointer
				/ Main.score.changes[0].length);

		System.out.println("Repeat = "
				+ repeat
				+ " Bar = "
				+ Main.score.barPointer
				+ " Key = "
				+ Main.score.key
				+ " Chord = "
				+ Main.score.changes[0][(Main.score.chordPointer)
						% Main.score.changes[0].length]);

		if (Main.score.instructions != null) {
			int instruction = findInstruction();
			// System.out.println("Instruction = " + instruction);
			// instruction:
			// 0=accompany with walking bass
			// 1=walking bass plus head with transformation
			// 2=walking bass plus head without transformation
			// 3=walking bass plus improvise

			switch (instruction) {
			case (0):
				learning = true;
				chordsDone = false;

				improvDone = true;
		 		WBDone = false;
		 		
				chords();
		 		walkingBass();

		 		break;
			case (1):
				learning = false;
				chordsDone = true;
			 	improvDone = false;
			 	WBDone = false;
				Main.score.head = true;
				Main.score.trans = true;
				improvise();
//				chords();
				walkingBass();
				break;
			case (2):
				learning = false;
				chordsDone = true;
			 	improvDone = true;
			 	WBDone = false;
				Main.score.head = true;
				Main.score.trans = false;
				improvise();
//				chords();
				walkingBass();
				break;
			case (3):
				learning = false;
				chordsDone = false;
			 	improvDone = false;
			 	WBDone = false;
				Main.score.head = false;
				Main.score.trans = false;
				improvise();
				chords();
				walkingBass();
				break;
			}

			if (instruction == 0) {

			}
			if (instruction == 1 || instruction == 2) {
				if (instruction == 1) {
					Main.score.head = true;
				}
				if (instruction == 2) {
					Main.score.head = false;
				}

			}
		} else {
			chords();
			Main.score.head = true;
		}
		int waitBar = 1920;
		while (!chordsDone||!improvDone||!WBDone) {
			try {
				Thread.sleep(1,0);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		waitBarScore = waitBar;
		return waitBar;
	}

	public void walkingBass() {
		WB = new WalkingBass();
		threadWB = new Thread(WB);
		threadWB.setPriority(Thread.MAX_PRIORITY);
		threadWB.start();
//		WalkingBass.run();

	}

	public void improvise() {
		improv = new Improvise();
		threadIM = new Thread(improv);
		threadIM.setPriority(Thread.MAX_PRIORITY);
		threadIM.start();
//		Improvise.run();
	}

	public void chords() {
//		chordsDone=true;
		chords = new Chords();
		thread = new Thread(chords);
		thread.setPriority(Thread.MAX_PRIORITY);
		thread.start();
//		Chords.run();
	}

	// find a chord in the score changes at a specific symbolic time
	public int[] findChord(int time) {
		int i = findChordIndex(time);
		int[] chord = { changes[0][i], changes[1][i] };
		return chord;
	}

	// find a chord index at a specific symbolic time
	public int findChordIndex(int time) {
		int timeCount = 0;
		int i = 0;
		// System.out.println("time = "+time+" timeCount = "+timeCount+" i = "+i+" change.length = "+changes.length);
		while (i < changes.length && timeCount < time) {
			if (timeCount <= time) {
				i++;
			}
			timeCount = timeCount + changes[2][i];

		}
		return i;
	}

	// convert an integer into an int[] where 1s mean the note is on and 0s the
	// note is off ... 32 notes above bass note.
	public int[] toChord(int chordInt) {
		String chord = Integer.toBinaryString(chordInt);
		char[] chordCharArray = chord.toCharArray();
		int[] chordArray = new int[chordCharArray.length];
		int j = 0;
		for (int i = 0; i < chordCharArray.length; i++) {
			chordArray[i] = (int) (chordCharArray[chordCharArray.length - i - 1]) - 48;
			// System.out.println("chord "+i+" = "+chordArray[i]);
			if (chordArray[i] == 1) {
				j++;
			}
		}
		int[] notesArray = new int[j];
		j = 0;
		for (int i = 0; i < chordCharArray.length; i++) {
			if (chordArray[i] == 1) {
				notesArray[j] = i;
				j++;
			}
		}
		return notesArray;
	}

	// concatenate Generalizable Objects
	@SuppressWarnings("unchecked")
	static <T> T[] concat(T[] a, T[] b) {
		final int alen = a.length;
		final int blen = b.length;
		final T[] result = (T[]) java.lang.reflect.Array.newInstance(a
				.getClass().getComponentType(), alen + blen);
		System.arraycopy(a, 0, result, 0, alen);
		System.arraycopy(b, 0, result, alen, blen);
		return result;
	}

	public static int convertDegreeToScale(int index) {
		int[] conversionChart = { 0, 2, 4, 5, 7, 9, 11 };
		int match = -1;
		for (int i = 0; i < conversionChart.length; i++) {
			if (index == conversionChart[i]) {
				match = i;
			}
		}
		if (match == -1) {
			System.out
					.println("!!!!!!!! illegal index for conversion -------> "
							+ index);
		}
		return match;

	}

	public int findInstruction() {
		int index = Main.score.instructions.length - 1;
		boolean bound = false;
		while (index >= 0 && !bound) {
			// System.out.println("TEST0");
			if (Main.score.repeat > Main.score.instructions[index][0]) {
				// System.out.println("TEST1");
				bound = true;
			} else if (Main.score.repeat == Main.score.instructions[index][0]
					&& Main.score.barPointer >= Main.score.instructions[index][1]) {
				// System.out.println("TEST2");
				bound = true;
			} else {
				index--;
			}
		}
		// System.out.println("INSTRUCTION: " +
		// Main.score.instructions[index][2]);
		return Main.score.instructions[index][2];
	}

}
